﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SCM.Common;
using SCM.Model.DBEntity;
using SCM.Model.Enums;
using SCM.Servie.IBusiness;
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;

namespace SCM.Manage.Middleware
{
    /// <summary>
    /// 异常处理中间件
    /// </summary>
    public class ExceptionMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ILogger logger;
        private readonly IWebHostEnvironment env;

        /// <summary>
        /// 构建中间件
        /// </summary>
        /// <param name="next"></param>
        /// <param name="logger"></param>
        /// <param name="env"></param>
        public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IWebHostEnvironment env)
        {
            this.next = next;
            this.logger = logger;
            this.env = env;
        }

        /// <summary>
        /// 异常调用
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next.Invoke(context);
                var features = context.Features;
            }
            catch (SCMException re)
            {
                await HandleException(context, re);
            }
            catch (BusinessException be)
            {
                await HandleBusinessException(context, be);
            }
            catch (Exception e)
            {
                await GeneralExceptionHandle(context, e);
            }
        }

        private async Task GeneralExceptionHandle(HttpContext context, Exception e)
        {
            context.Response.ContentType = "application/json;charset=utf-8";

            DataPack<object> viewData = new DataPack<object>();
            viewData.Code = ResultCode.Error;
            viewData.Message = e.Message;
            viewData.Data = null;

            var logService = ServiceLocator.Instance.GetService<ILogService>();
            var model = new T_ErrorLog();
            model.ID = Guid.NewGuid().ToString();
            model.Source = Assembly.GetExecutingAssembly().GetName().Name;
            model.ClientIP = context.Connection.RemoteIpAddress.MapToIPv4().ToString();
            model.Message = e.Message;
            model.CreateTime = DateTime.Now;
            model.ProcessFlag = ProcessFlagEnum.WaitHandle;
            using (var reader = new StreamReader(context.Request.Body))
            {
                reader.BaseStream.Position = 0;
                model.RequestData = reader.ReadToEnd();
            }

            model.RequestUrl = context.Request.GetDisplayUrl();
            model.UserName = context?.User?.Identity?.Name;
            model.StackTrace = e.StackTrace;
            logService.AddErrorLog(model);
            ILogger logger = ServiceLocator.Instance.GetRequiredService<ILogger<Filter.HttpResponseExceptionFilter>>();
            logger.LogError(e, e.Message);

            await context.Response.WriteAsync(JsonConvert.SerializeObject(viewData));
        }
        private async Task HandleBusinessException(HttpContext context, BusinessException e)
        {
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            context.Response.ContentType = "application/json;charset=utf-8";

            DataPack<object> viewData = new DataPack<object>();
            viewData.Code = e.ResultCode;
            viewData.Message = e.Message;
            viewData.Data = null;
            if (e.IsWriteLog)
            {
                var logService = ServiceLocator.Instance.GetService<ILogService>();
                var model = new T_BusinessExceptionLog();
                model.ID = Guid.NewGuid().ToString();
                model.ClientIP = context.Connection.RemoteIpAddress.MapToIPv4().ToString();
                model.Content = e.Message;
                model.CreateTime = DateTime.Now;
                model.ProcessFlag = ProcessFlagEnum.WaitHandle;
                model.RequestData = e.RequestData;
                model.RequestUrl = context.Request.GetDisplayUrl();
                model.UserName = context?.User?.Identity?.Name;
                logService.AddBusinessLog(model);
            }

            await context.Response.WriteAsync(JsonConvert.SerializeObject(viewData)).ConfigureAwait(true);
        }
        private async Task HandleException(HttpContext context, SCMException e)
        {
            context.Response.StatusCode = (int)e.HttpStatusCode;
            context.Response.ContentType = "application/json;charset=utf-8";

            DataPack<object> viewData = new DataPack<object>();
            viewData.Code = e.ResultCode;
            viewData.Message = e.Message;
            viewData.Data = e.Context;

            if (e.IsWriteLog)
            {
                logger.LogError(e, e.Message);
            }
            await context.Response.WriteAsync(JsonConvert.SerializeObject(viewData)).ConfigureAwait(true);
        }
    }
}
